////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2017 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file camxthreadcore.h
///
/// @brief Provides core worker thread functionality
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef CAMXTHREADCORE_H
#define CAMXTHREADCORE_H

#include "camxtypes.h"
#include "camxatomic.h"
#include "camxosutils.h"
#include "camxthreadcommon.h"
#include "camxthreadjoblist.h"
#include "camxthreadqueue.h"

CAMX_NAMESPACE_BEGIN

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @brief ThreadCore provides the core worker thread functionality
///
/// Creates worker threads, add jobs to queues, dispatches jobs, invokes callbacks
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class ThreadCore
{
public:
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ThreadCore
    ///
    /// @brief  Constructor for ThreadCore object
    ///
    /// @param  pJobRegistry Pointer to the job registry
    /// @param  pJobList     Pointer to the job list
    /// @param  pName        Name of the pool
    /// @param  numThreads   Number of worker threads in the pool
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ThreadCore(
        JobRegistry* pJobRegistry,
        JobList*     pJobList,
        const CHAR*  pName,
        UINT32       numThreads);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ~ThreadCore
    ///
    /// @brief  Destructor for ThreadCore object
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    ~ThreadCore();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// AcceptNewJob
    ///
    /// @brief  Post a job, which may belong to a previously registered family
    ///
    /// @param  hJob         Handle to the job returned from RegisterJobFamily
    /// @param  stoppedCb    Job stopped CB for this job, If the job family is flushed
    /// @param  ppData       Array or 1 or more partitioned data, terminated by NULL pointer
    /// @param  isSplitable  If the job can be partitioned
    /// @param  isBlocking   If the caller of the job blocks on its completion
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult AcceptNewJob(
        JobHandle   hJob,
        JobCb       stoppedCb,
        VOID**      ppData,
        BOOL        isSplitable,
        BOOL        isBlocking);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FlushJob
    ///
    /// @brief  Flush a registered family of job
    ///
    /// @param  hJob         Handle to the job returned from RegisterJobFamily
    /// @param  pUserData    User data passed back in flushDoneCb, which thread library doesn't look into
    /// @param  isBlocking   If the caller of the flush blocks on its completion
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult FlushJob(
        JobHandle   hJob,
        VOID*       pUserData,
        BOOL        isBlocking);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetJobCount
    ///
    /// @brief  Retrieve current job count of a registered job
    ///
    /// @param  hJob Handle to previously registered job
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE UINT32 GetJobCount(
        JobHandle  hJob) const
    {
        CAMX_ASSERT_MESSAGE(((Initialized == GetStatus()) && (TRUE == m_pJobregistry->ValidateJob(hJob))),
                            "Can not get job count");

        return m_pJobregistry->GetJobCount(hJob);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetInFlightCount
    ///
    /// @brief  Retrieve number of jobs executing currently
    ///
    /// @param  hJob Handle to previously registered job
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE UINT32 GetInFlightCount(
        JobHandle  hJob) const
    {
        CAMX_ASSERT_MESSAGE(((Initialized == GetStatus()) && (TRUE == m_pJobregistry->ValidateJob(hJob))),
                            "Can not get job count");

        return m_pJobregistry->GetInflightCount(hJob);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Initialize
    ///
    /// @brief  Initialize a newly created ThreadCore object
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult Initialize();

private:
    // Disable copy constructor and assignment operator
    ThreadCore(const ThreadCore&) = delete;
    ThreadCore& operator=(const ThreadCore&) = delete;

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetStatus
    ///
    /// @brief  Get the current status of the thread library core
    ///
    /// @return CoreStatus
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE CoreStatus GetStatus() const
    {
        // NOWHINE CP036a: Since the function is const, had to add the const_cast
        return CamxAtomicLoadU8(const_cast<volatile UINT8*>(&m_status));
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// SetStatus
    ///
    /// @brief  Set the current status of the thread library core
    ///
    /// @param  status Value to set
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CAMX_INLINE VOID SetStatus(
        CoreStatus status)
    {
        CamxAtomicStoreU8(&m_status, status);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// WorkerThreadBody
    ///
    /// @brief  Static, common landing function for all worker threads after creation
    ///
    /// @param  pArg Payload for the worker thread
    ///
    /// @return NULL
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    static VOID* WorkerThreadBody(
        VOID* pArg);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// DoWork
    ///
    /// @brief  Actual worker thread routine
    ///
    /// @return NULL
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID* DoWork();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// ProcessJobQueue
    ///
    /// @brief  Main routine for worker threads to look into and execute job queues in order of priority
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult ProcessJobQueue();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// AddToPriorityQueue
    ///
    /// @brief  Adds a runtime job to one of the job queues, based on priority
    ///
    /// @param  pJob Pointer to the runtime job
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult AddToPriorityQueue(
        RuntimeJob* pJob);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// GetQueue
    ///
    /// @brief  Get the specific job queue based on priority
    ///
    /// @param  priority One of Critical, High or Normal
    ///
    /// @return Pointer to a JobQueue or NULL
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    JobQueue* GetQueue(
        JobPriority priority);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// DispatchJob
    ///
    /// @brief  Dispatch a job from the thread pool by calling its function
    ///
    /// @param  pJob Pointer to the runtime job
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID DispatchJob(
        RuntimeJob* pJob);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// Trigger
    ///
    /// @brief  Trigger threads to process job queues
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID Trigger();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// OnJobStopped
    ///
    /// @brief  Call stoppedCb, the job family is being flushed
    ///
    /// @param  pJob Pointer to the runtime job
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID OnJobStopped(
        RuntimeJob* pJob);

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// FlushAllJobsInternal
    ///
    /// @brief  Go through all registered jobs one by one, and flush them, blocking on each flush
    ///
    /// @return None
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    VOID FlushAllJobsInternal();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// StartThreads
    ///
    /// @brief  Create & start worker threads, initialize mutex and conditions
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult StartThreads();

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// StopThreads
    ///
    /// @brief  Stop worker threads, and de-initialize mutex and condition
    ///
    /// @return Success or EFailed
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    CamxResult StopThreads();

    /// @brief Max size for a given thread name including instance id
    static const INT ThreadNameLength = 255;

    CHAR            m_name[ThreadNameLength];       ///< Brief human readable name for the pool
    JobRegistry*    m_pJobregistry;                 ///< Pointer to job registry
    JobList*        m_pJobList;                     ///< Pointer to job list
    UINT32          m_numThreads;                   ///< Number of worker threads in the pool
    volatile BOOL   m_stopped;                      ///< Indicates whether the threads should stop running
    volatile BOOL   m_jobPending;                   ///< Indicates if a new job is submitted
    JobQueue        m_jobQueues[MaxNumQueues];      ///< Priority Job Queues - 0 = critical, 1 = high and 2 = normal

    Mutex*          m_pThreadLock;                  ///< Single lock for all threads, covers m_pReadOK, m_stopped, m_jobPending
    Condition*      m_pReadOK;                      ///< Single condition for all threads

    CoreStatus      m_status;                       ///< Overall status of the core (one of Error, Inited or Stopped)
    ThreadConfig    m_workers[MaxThreadsPerPool];   ///< Actual worker thread configurations
};

CAMX_NAMESPACE_END

#endif // CAMXTHREADCORE_H
